package io.ebean.config;

import com.fasterxml.jackson.core.JsonFactory;
import io.avaje.config.Config;
import io.ebean.*;
import io.ebean.annotation.MutationDetection;
import io.ebean.annotation.PersistBatch;
import io.ebean.annotation.Platform;
import io.ebean.cache.ServerCachePlugin;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbEncrypt;
import io.ebean.config.dbplatform.DbType;
import io.ebean.config.dbplatform.IdType;
import io.ebean.datasource.DataSourceBuilder;
import io.ebean.event.*;
import io.ebean.event.changelog.ChangeLogListener;
import io.ebean.event.changelog.ChangeLogPrepare;
import io.ebean.event.changelog.ChangeLogRegister;
import io.ebean.event.readaudit.ReadAuditLogger;
import io.ebean.event.readaudit.ReadAuditPrepare;
import io.ebean.meta.MetricNamingMatch;
import io.ebean.util.StringHelper;
import jakarta.persistence.EnumType;

import javax.sql.DataSource;
import java.time.Clock;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * The configuration used for creating a Database.
 * <p>
 * Used to programmatically construct an Database and optionally register it
 * with the DB singleton.
 * <p>
 * If you just use DB thout this programmatic configuration Ebean will read
 * the application.properties file and take the configuration from there. This usually
 * includes searching the class path and automatically registering any entity
 * classes and listeners etc.
 * <pre>{@code
 *
 * DatabaseConfig config = new DatabaseConfig();
 *
 * // read the ebean.properties and load
 * // those settings into this DatabaseConfig object
 * config.loadFromProperties();
 *
 * // explicitly register the entity beans to avoid classpath scanning
 * config.addClass(Customer.class);
 * config.addClass(User.class);
 *
 * Database db = DatabaseFactory.create(config);
 *
 * }</pre>
 *
 * <p>
 * Note that DatabaseConfigProvider provides a standard Java ServiceLoader mechanism that can
 * be used to apply configuration to the DatabaseConfig.
 *
 * @author emcgreal
 * @author rbygrave
 * @see DatabaseFactory
 */
public class DatabaseConfig implements DatabaseBuilder.Settings {

  /**
   * The Database name.
   */
  private String name = "db";

  /**
   * Typically configuration type objects that are passed by this DatabaseConfig
   * to plugins. For example - IgniteConfiguration passed to Ignite plugin.
   */
  private final Map<String, Object> serviceObject = new HashMap<>();

  private ContainerConfig containerConfig;

  /**
   * The underlying properties that were used during configuration.
   */
  private Properties properties;

  /**
   * The resource directory.
   */
  private String resourceDirectory;

  /**
   * Set to true to register this Database with the DB singleton.
   */
  private boolean register = true;

  /**
   * Set to true if this is the default/primary database.
   */
  private boolean defaultServer = true;

  /**
   * Set this to true to disable class path search.
   */
  private boolean disableClasspathSearch;

  private TenantMode tenantMode = TenantMode.NONE;

  private String tenantPartitionColumn = "tenant_id";

  private CurrentTenantProvider currentTenantProvider;

  private TenantDataSourceProvider tenantDataSourceProvider;

  private TenantSchemaProvider tenantSchemaProvider;

  private TenantCatalogProvider tenantCatalogProvider;

  /**
   * When true will load entity classes via EntityClassRegister.
   * <p>
   * NB: EntityClassRegister implementations are generated by querybean generator.
   * Having this on and registering entity classes means we don't need to manually
   * write that code or use classpath scanning to find entity classes.
   */
  private boolean loadModuleInfo = true;

  /**
   * When true then include a sql comment in generated SELECT queries with the query
   * label or profile location label.
   */
  private boolean includeLabelInSql = true;

  /**
   * Interesting classes such as entities, embedded, ScalarTypes,
   * Listeners, Finders, Controllers, AttributeConverters etc.
   */
  private Set<Class<?>> classes = new HashSet<>();

  /**
   * The packages that are searched for interesting classes. Only used when
   * classes is empty/not explicitly specified.
   */
  private List<String> packages = new ArrayList<>();

  /**
   * Configuration for the ElasticSearch integration.
   */
  private DocStoreConfig docStoreConfig = new DocStoreConfig();

  /**
   * Set to true when the Database only uses Document store.
   */
  private boolean docStoreOnly;

  /**
   * This is used to populate @WhoCreated, @WhoModified and
   * support other audit features (who executed a query etc).
   */
  private CurrentUserProvider currentUserProvider;

  /**
   * Config controlling the AutoTune behaviour.
   */
  private AutoTuneConfig autoTuneConfig = new AutoTuneConfig();

  /**
   * The JSON format used for DateTime types. Default to millis.
   */
  private JsonConfig.DateTime jsonDateTime = JsonConfig.DateTime.ISO8601;

  /**
   * The JSON format used for Date types. Default to millis.
   */
  private JsonConfig.Date jsonDate = JsonConfig.Date.ISO8601;

  /**
   * For writing JSON specify if null values or empty collections should be excluded.
   * By default all values are included.
   */
  private JsonConfig.Include jsonInclude = JsonConfig.Include.ALL;

  /**
   * The default mode used for {@code @DbJson} with Jackson ObjectMapper.
   */
  private MutationDetection jsonMutationDetection = MutationDetection.HASH;

  /**
   * The database platform name. Used to imply a DatabasePlatform to use.
   */
  private String databasePlatformName;

  /**
   * The database platform.
   */
  private DatabasePlatform databasePlatform;

  /**
   * JDBC fetchSize hint when using findList.  Defaults to 0 leaving it up to the JDBC driver.
   */
  private int jdbcFetchSizeFindList;

  /**
   * JDBC fetchSize hint when using findEach/findEachWhile.  Defaults to 100. Note that this does
   * not apply to MySql as that gets special treatment (forward only etc).
   */
  private int jdbcFetchSizeFindEach = 100;

  /**
   * Suffix appended to the base table to derive the view that contains the union
   * of the base table and the history table in order to support asOf queries.
   */
  private String asOfViewSuffix = "_with_history";

  /**
   * Column used to support history and 'As of' queries. This column is a timestamp range
   * or equivalent.
   */
  private String asOfSysPeriod = "sys_period";

  /**
   * Suffix appended to the base table to derive the view that contains the union
   * of the base table and the history table in order to support asOf queries.
   */
  private String historyTableSuffix = "_history";

  /**
   * When true explicit transactions beans that have been made dirty will be
   * automatically persisted via update on flush.
   */
  private boolean autoPersistUpdates;

  /**
   * Use for transaction scoped batch mode.
   */
  private PersistBatch persistBatch = PersistBatch.NONE;

  /**
   * Use for cascade persist JDBC batch mode. INHERIT means use the platform default
   * which is ALL except for SQL Server where it is NONE (as getGeneratedKeys isn't
   * supported on SQL Server with JDBC batch).
   */
  private PersistBatch persistBatchOnCascade = PersistBatch.INHERIT;

  private int persistBatchSize = 100;

  private EnumType defaultEnumType = EnumType.ORDINAL;

  private boolean disableLazyLoading;

  /**
   * The default batch size for lazy loading
   */
  private int lazyLoadBatchSize = 100;

  /**
   * The default batch size for 'query joins'.
   */
  private int queryBatchSize = 100;

  private boolean eagerFetchLobs;

  /**
   * Timezone used to get/set Timestamp values via JDBC.
   */
  private String dataTimeZone;

  private boolean ddlGenerate;

  private boolean ddlRun;

  private boolean ddlExtra = true;

  private boolean ddlCreateOnly;

  private String ddlInitSql;

  private String ddlSeedSql;

  private String ddlHeader;

  /**
   * Mode used to check non-null columns added via migration have a default value specified etc.
   */
  private boolean ddlStrictMode = true;

  /**
   * Comma and equals delimited key/value placeholders to replace in DDL scripts.
   */
  private String ddlPlaceholders;

  /**
   * Map of key/value placeholders to replace in DDL scripts.
   */
  private Map<String, String> ddlPlaceholderMap;

  private boolean runMigration;

  /**
   * When true L2 bean cache use is skipped after a write has occurred on a transaction.
   */
  private boolean skipCacheAfterWrite = true;

  private boolean useJtaTransactionManager;

  /**
   * The external transaction manager (like Spring).
   */
  private ExternalTransactionManager externalTransactionManager;

  private boolean skipDataSourceCheck;

  private boolean readOnlyDatabase;
  private boolean shutdownHook = true;

  /**
   * The data source (if programmatically provided).
   */
  private DataSource dataSource;

  /**
   * The read only data source (can be null).
   */
  private DataSource readOnlyDataSource;

  /**
   * The data source config.
   */
  private DataSourceBuilder.Settings dataSourceConfig = DataSourceBuilder.create().settings();

  /**
   * When true create a read only DataSource using readOnlyDataSourceConfig defaulting values from dataSourceConfig.
   * I believe this will default to true in some future release (as it has a nice performance benefit).
   * <p>
   * autoReadOnlyDataSource is an unfortunate name for this config option but I haven't come up with a better one.
   */
  private boolean autoReadOnlyDataSource;

  /**
   * Optional configuration for a read only data source.
   */
  private DataSourceBuilder.Settings readOnlyDataSourceConfig = DataSourceBuilder.create().settings();

  /**
   * Optional - the database schema that should be used to own the tables etc.
   */
  private String dbSchema;

  /**
   * The ClassLoadConfig used to detect Joda, Java8, Jackson etc and create plugin instances given a className.
   */
  private ClassLoadConfig classLoadConfig = new ClassLoadConfig();

  /**
   * The naming convention.
   */
  private NamingConvention namingConvention = new UnderscoreNamingConvention();

  private AggregateFormulaContext aggregateFormulaContext = AggregateFormulaContext.builder().build();

  /**
   * Behaviour of updates in JDBC batch to by default include all properties.
   */
  private boolean updateAllPropertiesInBatch;

  /**
   * Database platform configuration.
   */
  private PlatformConfig platformConfig = new PlatformConfig();

  /**
   * The UUID version to use.
   */
  private UuidVersion uuidVersion = UuidVersion.VERSION4;

  /**
   * The UUID state file (for Version 1 UUIDs). By default, the file is created in
   * ${HOME}/.ebean/${servername}-uuid.state
   */
  private String uuidStateFile;

  /**
   * The node id (=mac address) for Version 1 UUIDs. There are several options:
   * <ul>
   * <li><code>null</code> (default) The generator tries to get the hardwarwe MAC
   * address. If this fails, it will fall back to 'generate' mode.</li>
   * <li><code>"generate"</code> Hardware detection is skipped. It generates a
   * random identifier and tries to persist this to the state file. This nodeId
   * will be reused on next start. If persisting to the state file will fail also,
   * it will fall back to 'random' mode.<br>
   * This mode is good, if the MAC address is not reliable, e.g. if you run
   * multiple ebean instances on the same machine.</li>
   * <li><code>"random"</code> In this mode, a random node id is generated on each
   * start. No stateFile is used. it will generate a new nodeId on each
   * application start.<br>
   * This mode is good, if you have no write access to save the state file.</li>
   * <li><code>"xx-xx-xx-xx-xx-xx"</code> When an explicit nodeId is specified,
   * this one is used.</li>
   * </ul>
   * Note: It is possible that multiple servers are sharing the same state file as
   * long as they are in the <b>same</b> JVM/ClassLoader scope. In this case it is
   * recommended to use the same uuidNodeId configuration.
   * <p>
   * If you have multiple servers in different JVMs, do <b>not</b> share the state
   * files!
   */
  private String uuidNodeId;

  /**
   * The clock used for setting the timestamps (e.g. @UpdatedTimestamp) on objects.
   */
  private Clock clock = Clock.systemUTC();

  private List<IdGenerator> idGenerators = new ArrayList<>();
  private List<BeanFindController> findControllers = new ArrayList<>();
  private List<BeanPersistController> persistControllers = new ArrayList<>();
  private List<BeanPostLoad> postLoaders = new ArrayList<>();
  private List<BeanPostConstructListener> postConstructListeners = new ArrayList<>();
  private List<BeanPersistListener> persistListeners = new ArrayList<>();
  private List<BeanQueryAdapter> queryAdapters = new ArrayList<>();
  private final List<BulkTableEventListener> bulkTableEventListeners = new ArrayList<>();
  private final List<ServerConfigStartup> configStartupListeners = new ArrayList<>();

  /**
   * By default inserts are included in the change log.
   */
  private boolean changeLogIncludeInserts = true;

  private ChangeLogPrepare changeLogPrepare;
  private ChangeLogListener changeLogListener;
  private ChangeLogRegister changeLogRegister;
  private boolean changeLogAsync = true;
  private ReadAuditLogger readAuditLogger;
  private ReadAuditPrepare readAuditPrepare;
  private EncryptKeyManager encryptKeyManager;
  private EncryptDeployManager encryptDeployManager;
  private Encryptor encryptor;
  private DbEncrypt dbEncrypt;
  private boolean dbOffline;
  private ServerCachePlugin serverCachePlugin;

  /**
   * The default PersistenceContextScope used if one is not explicitly set on a query.
   */
  private PersistenceContextScope persistenceContextScope = PersistenceContextScope.TRANSACTION;
  private JsonFactory jsonFactory;
  private boolean localTimeWithNanos;
  private boolean durationWithNanos;
  private int maxCallStack = 5;
  private boolean transactionRollbackOnChecked = true;

  // configuration for the background executor service (thread pool)

  private int backgroundExecutorSchedulePoolSize = 1;
  private int backgroundExecutorShutdownSecs = 30;
  private BackgroundExecutorWrapper backgroundExecutorWrapper = new MdcBackgroundExecutorWrapper();

  // defaults for the L2 bean caching

  private int cacheMaxSize = 10000;
  private int cacheMaxIdleTime = 600;
  private int cacheMaxTimeToLive = 60 * 60 * 6;

  // defaults for the L2 query caching

  private int queryCacheMaxSize = 1000;
  private int queryCacheMaxIdleTime = 600;
  private int queryCacheMaxTimeToLive = 60 * 60 * 6;
  private Object objectMapper;

  /**
   * Set to true if you want eq("someProperty", null) to generate 1=1 rather than "is null" sql expression.
   */
  private boolean expressionEqualsWithNullAsNoop;

  /**
   * Set to true to use native ILIKE expression (if support by database platform / like Postgres).
   */
  private boolean expressionNativeIlike;

  private String jodaLocalTimeMode;

  /**
   * Time to live for query plans - defaults to 5 minutes.
   */
  private int queryPlanTTLSeconds = 60 * 5;
  private String queryPlanExplain;

  /**
   * Set to true to globally disable L2 caching (typically for performance testing).
   */
  private boolean disableL2Cache;

  private String enabledL2Regions;

  /**
   * Set to true to effectively disable L2 cache plugins.
   */
  private boolean localOnlyL2Cache;

  /**
   * Should the javax.validation.constraints.NotNull enforce a notNull column in DB.
   * If set to false, use io.ebean.annotation.NotNull or Column(nullable=true).
   */
  private boolean useValidationNotNull = true;

  /**
   * Generally we want to perform L2 cache notification in the background and not impact
   * the performance of executing transactions.
   */
  private boolean notifyL2CacheInForeground;

  /**
   * Set to true to enable bind capture required for query plan capture.
   */
  private boolean queryPlanEnable;

  /**
   * The default threshold in micros for collecting query plans.
   */
  private long queryPlanThresholdMicros = Long.MAX_VALUE;

  /**
   * Set to true to enable automatic periodic query plan capture.
   */
  private boolean queryPlanCapture;
  private long queryPlanCapturePeriodSecs = 60 * 10; // 10 minutes
  private long queryPlanCaptureMaxTimeMillis = 10_000; // 10 seconds
  private int queryPlanCaptureMaxCount = 10;
  private QueryPlanListener queryPlanListener;

  /**
   * The time in millis used to determine when a query is alerted for being slow.
   */
  private long slowQueryMillis;

  /**
   * The listener for processing slow query events.
   */
  private SlowQueryListener slowQueryListener;

  private ProfilingConfig profilingConfig = new ProfilingConfig();

  /**
   * The mappingLocations for searching xml mapping.
   */
  private List<String> mappingLocations = new ArrayList<>();

  /**
   * When true we do not need explicit GeneratedValue mapping.
   */
  private boolean idGeneratorAutomatic = true;

  private boolean dumpMetricsOnShutdown;

  private String dumpMetricsOptions;

  private LengthCheck lengthCheck = LengthCheck.OFF;

  private Function<String, String> metricNaming = MetricNamingMatch.INSTANCE;

  /**
   * Construct a Database Configuration for programmatically creating an Database.
   */
  public DatabaseConfig() {
  }

  @Override
  public Database build() {
    return DatabaseFactory.create(this);
  }

  @Override
  public Settings settings() {
    return this;
  }

  @Override
  public DatabaseBuilder apply(Consumer<DatabaseBuilder.Settings> applyConfiguration) {
    applyConfiguration.accept(this);
    return this;
  }

  @Override
  public Clock getClock() {
    return clock;
  }

  @Override
  public DatabaseConfig setClock(final Clock clock) {
    this.clock = clock;
    return this;
  }

  @Override
  public long getSlowQueryMillis() {
    return slowQueryMillis;
  }

  @Override
  public DatabaseConfig setSlowQueryMillis(long slowQueryMillis) {
    this.slowQueryMillis = slowQueryMillis;
    return this;
  }

  @Override
  public SlowQueryListener getSlowQueryListener() {
    return slowQueryListener;
  }

  @Override
  public DatabaseConfig setSlowQueryListener(SlowQueryListener slowQueryListener) {
    this.slowQueryListener = slowQueryListener;
    return this;
  }

  @Override
  public DatabaseConfig putServiceObject(String key, Object configObject) {
    serviceObject.put(key, configObject);
    return this;
  }

  @Override
  public <T> DatabaseConfig putServiceObject(Class<T> iface, T configObject) {
    serviceObject.put(serviceObjectKey(iface), configObject);
    return this;
  }

  @Override
  public Object getServiceObject(String key) {
    return serviceObject.get(key);
  }

  @Override
  public DatabaseConfig putServiceObject(Object configObject) {
    String key = serviceObjectKey(configObject);
    serviceObject.put(key, configObject);
    return this;
  }

  @Override
  @SuppressWarnings("unchecked")
  public <P> P getServiceObject(Class<P> cls) {
    return (P) serviceObject.get(serviceObjectKey(cls));
  }

  private String serviceObjectKey(Object configObject) {
    return serviceObjectKey(configObject.getClass());
  }

  private String serviceObjectKey(Class<?> cls) {
    String simpleName = cls.getSimpleName();
    return Character.toLowerCase(simpleName.charAt(0)) + simpleName.substring(1);
  }

  @Override
  public JsonFactory getJsonFactory() {
    return jsonFactory;
  }

  @Override
  public DatabaseConfig setJsonFactory(JsonFactory jsonFactory) {
    this.jsonFactory = jsonFactory;
    return this;
  }

  @Override
  public JsonConfig.DateTime getJsonDateTime() {
    return jsonDateTime;
  }

  @Override
  public DatabaseConfig setJsonDateTime(JsonConfig.DateTime jsonDateTime) {
    this.jsonDateTime = jsonDateTime;
    return this;
  }

  @Override
  public JsonConfig.Date getJsonDate() {
    return jsonDate;
  }

  @Override
  public DatabaseConfig setJsonDate(JsonConfig.Date jsonDate) {
    this.jsonDate = jsonDate;
    return this;
  }

  @Override
  public JsonConfig.Include getJsonInclude() {
    return jsonInclude;
  }

  @Override
  public DatabaseConfig setJsonInclude(JsonConfig.Include jsonInclude) {
    this.jsonInclude = jsonInclude;
    return this;
  }

  @Override
  public MutationDetection getJsonMutationDetection() {
    return jsonMutationDetection;
  }

  @Override
  public DatabaseConfig setJsonMutationDetection(MutationDetection jsonMutationDetection) {
    this.jsonMutationDetection = jsonMutationDetection;
    return this;
  }

  @Override
  public String getName() {
    return name;
  }

  @Override
  public DatabaseConfig setName(String name) {
    this.name = name;
    return this;
  }

  @Override
  public ContainerConfig getContainerConfig() {
    return containerConfig;
  }

  @Override
  public DatabaseConfig setContainerConfig(ContainerConfig containerConfig) {
    this.containerConfig = containerConfig;
    return this;
  }

  @Override
  public boolean isRegister() {
    return register;
  }

  @Override
  public DatabaseConfig setRegister(boolean register) {
    this.register = register;
    return this;
  }

  @Override
  public boolean isDefaultServer() {
    return defaultServer;
  }

  @Override
  public DatabaseConfig setDefaultServer(boolean defaultServer) {
    this.defaultServer = defaultServer;
    return this;
  }

  @Override
  public CurrentUserProvider getCurrentUserProvider() {
    return currentUserProvider;
  }

  @Override
  public DatabaseConfig setCurrentUserProvider(CurrentUserProvider currentUserProvider) {
    this.currentUserProvider = currentUserProvider;
    return this;
  }

  @Override
  public TenantMode getTenantMode() {
    return tenantMode;
  }

  @Override
  public DatabaseConfig setTenantMode(TenantMode tenantMode) {
    this.tenantMode = tenantMode;
    return this;
  }

  @Override
  public String getTenantPartitionColumn() {
    return tenantPartitionColumn;
  }

  @Override
  public DatabaseConfig setTenantPartitionColumn(String tenantPartitionColumn) {
    this.tenantPartitionColumn = tenantPartitionColumn;
    return this;
  }

  @Override
  public CurrentTenantProvider getCurrentTenantProvider() {
    return currentTenantProvider;
  }

  @Override
  public DatabaseConfig setCurrentTenantProvider(CurrentTenantProvider currentTenantProvider) {
    this.currentTenantProvider = currentTenantProvider;
    return this;
  }

  @Override
  public TenantDataSourceProvider getTenantDataSourceProvider() {
    return tenantDataSourceProvider;
  }

  @Override
  public DatabaseConfig setTenantDataSourceProvider(TenantDataSourceProvider tenantDataSourceProvider) {
    this.tenantDataSourceProvider = tenantDataSourceProvider;
    return this;
  }

  @Override
  public TenantSchemaProvider getTenantSchemaProvider() {
    return tenantSchemaProvider;
  }

  @Override
  public DatabaseConfig setTenantSchemaProvider(TenantSchemaProvider tenantSchemaProvider) {
    this.tenantSchemaProvider = tenantSchemaProvider;
    return this;
  }

  @Override
  public TenantCatalogProvider getTenantCatalogProvider() {
    return tenantCatalogProvider;
  }

  @Override
  public DatabaseConfig setTenantCatalogProvider(TenantCatalogProvider tenantCatalogProvider) {
    this.tenantCatalogProvider = tenantCatalogProvider;
    return this;
  }

  @Override
  public boolean isAutoPersistUpdates() {
    return autoPersistUpdates;
  }

  @Override
  public DatabaseConfig setAutoPersistUpdates(boolean autoPersistUpdates) {
    this.autoPersistUpdates = autoPersistUpdates;
    return this;
  }

  @Override
  public PersistBatch getPersistBatch() {
    return persistBatch;
  }

  @Override
  public DatabaseConfig setPersistBatch(PersistBatch persistBatch) {
    this.persistBatch = persistBatch;
    return this;
  }

  @Override
  public PersistBatch getPersistBatchOnCascade() {
    return persistBatchOnCascade;
  }

  @Override
  public DatabaseConfig setPersistBatchOnCascade(PersistBatch persistBatchOnCascade) {
    this.persistBatchOnCascade = persistBatchOnCascade;
    return this;
  }

  @Override
  public DatabaseConfig setPersistBatching(boolean persistBatching) {
    this.persistBatch = (persistBatching) ? PersistBatch.ALL : PersistBatch.NONE;
    return this;
  }

  @Override
  public int getPersistBatchSize() {
    return persistBatchSize;
  }

  @Override
  public DatabaseConfig setPersistBatchSize(int persistBatchSize) {
    this.persistBatchSize = persistBatchSize;
    return this;
  }

  @Override
  public int getQueryBatchSize() {
    return queryBatchSize;
  }

  @Override
  public DatabaseConfig setQueryBatchSize(int queryBatchSize) {
    this.queryBatchSize = queryBatchSize;
    return this;
  }

  @Override
  public EnumType getDefaultEnumType() {
    return defaultEnumType;
  }

  @Override
  public DatabaseConfig setDefaultEnumType(EnumType defaultEnumType) {
    this.defaultEnumType = defaultEnumType;
    return this;
  }

  @Override
  public boolean isDisableLazyLoading() {
    return disableLazyLoading;
  }

  @Override
  public DatabaseConfig setDisableLazyLoading(boolean disableLazyLoading) {
    this.disableLazyLoading = disableLazyLoading;
    return this;
  }

  @Override
  public int getLazyLoadBatchSize() {
    return lazyLoadBatchSize;
  }

  @Override
  public DatabaseConfig setLazyLoadBatchSize(int lazyLoadBatchSize) {
    this.lazyLoadBatchSize = lazyLoadBatchSize;
    return this;
  }

  @Override
  public DatabaseConfig setDatabaseSequenceBatchSize(int databaseSequenceBatchSize) {
    platformConfig.setDatabaseSequenceBatchSize(databaseSequenceBatchSize);
    return this;
  }

  @Override
  public int getJdbcFetchSizeFindList() {
    return jdbcFetchSizeFindList;
  }

  @Override
  public DatabaseConfig setJdbcFetchSizeFindList(int jdbcFetchSizeFindList) {
    this.jdbcFetchSizeFindList = jdbcFetchSizeFindList;
    return this;
  }

  @Override
  public int getJdbcFetchSizeFindEach() {
    return jdbcFetchSizeFindEach;
  }

  @Override
  public DatabaseConfig setJdbcFetchSizeFindEach(int jdbcFetchSizeFindEach) {
    this.jdbcFetchSizeFindEach = jdbcFetchSizeFindEach;
    return this;
  }

  @Override
  public ChangeLogPrepare getChangeLogPrepare() {
    return changeLogPrepare;
  }

  @Override
  public DatabaseConfig setChangeLogPrepare(ChangeLogPrepare changeLogPrepare) {
    this.changeLogPrepare = changeLogPrepare;
    return this;
  }

  @Override
  public ChangeLogListener getChangeLogListener() {
    return changeLogListener;
  }

  @Override
  public DatabaseConfig setChangeLogListener(ChangeLogListener changeLogListener) {
    this.changeLogListener = changeLogListener;
    return this;
  }

  @Override
  public ChangeLogRegister getChangeLogRegister() {
    return changeLogRegister;
  }

  @Override
  public DatabaseConfig setChangeLogRegister(ChangeLogRegister changeLogRegister) {
    this.changeLogRegister = changeLogRegister;
    return this;
  }

  @Override
  public boolean isChangeLogIncludeInserts() {
    return changeLogIncludeInserts;
  }

  @Override
  public DatabaseConfig setChangeLogIncludeInserts(boolean changeLogIncludeInserts) {
    this.changeLogIncludeInserts = changeLogIncludeInserts;
    return this;
  }

  @Override
  public boolean isChangeLogAsync() {
    return changeLogAsync;
  }

  @Override
  public DatabaseConfig setChangeLogAsync(boolean changeLogAsync) {
    this.changeLogAsync = changeLogAsync;
    return this;
  }

  @Override
  public ReadAuditLogger getReadAuditLogger() {
    return readAuditLogger;
  }

  @Override
  public DatabaseConfig setReadAuditLogger(ReadAuditLogger readAuditLogger) {
    this.readAuditLogger = readAuditLogger;
    return this;
  }

  @Override
  public ReadAuditPrepare getReadAuditPrepare() {
    return readAuditPrepare;
  }

  @Override
  public DatabaseConfig setReadAuditPrepare(ReadAuditPrepare readAuditPrepare) {
    this.readAuditPrepare = readAuditPrepare;
    return this;
  }

  @Override
  public ProfilingConfig getProfilingConfig() {
    return profilingConfig;
  }

  @Override
  public DatabaseConfig setProfilingConfig(ProfilingConfig profilingConfig) {
    this.profilingConfig = profilingConfig;
    return this;
  }

  @Override
  public String getDbSchema() {
    return dbSchema;
  }

  @Override
  public DatabaseConfig setDbSchema(String dbSchema) {
    this.dbSchema = dbSchema;
    return this;
  }

  @Override
  public int getGeometrySRID() {
    return platformConfig.getGeometrySRID();
  }

  @Override
  public DatabaseConfig setGeometrySRID(int geometrySRID) {
    platformConfig.setGeometrySRID(geometrySRID);
    return this;
  }

  @Override
  public String getDataTimeZone() {
    return System.getProperty("ebean.dataTimeZone", dataTimeZone);
  }

  @Override
  public DatabaseConfig setDataTimeZone(String dataTimeZone) {
    this.dataTimeZone = dataTimeZone;
    return this;
  }

  @Override
  public String getAsOfViewSuffix() {
    return asOfViewSuffix;
  }

  @Override
  public DatabaseConfig setAsOfViewSuffix(String asOfViewSuffix) {
    this.asOfViewSuffix = asOfViewSuffix;
    return this;
  }

  @Override
  public String getAsOfSysPeriod() {
    return asOfSysPeriod;
  }

  @Override
  public DatabaseConfig setAsOfSysPeriod(String asOfSysPeriod) {
    this.asOfSysPeriod = asOfSysPeriod;
    return this;
  }

  @Override
  public String getHistoryTableSuffix() {
    return historyTableSuffix;
  }

  @Override
  public DatabaseConfig setHistoryTableSuffix(String historyTableSuffix) {
    this.historyTableSuffix = historyTableSuffix;
    return this;
  }

  @Override
  public boolean isUseJtaTransactionManager() {
    return useJtaTransactionManager;
  }

  @Override
  public DatabaseConfig setUseJtaTransactionManager(boolean useJtaTransactionManager) {
    this.useJtaTransactionManager = useJtaTransactionManager;
    return this;
  }

  @Override
  public ExternalTransactionManager getExternalTransactionManager() {
    return externalTransactionManager;
  }

  @Override
  public DatabaseConfig setExternalTransactionManager(ExternalTransactionManager externalTransactionManager) {
    this.externalTransactionManager = externalTransactionManager;
    return this;
  }

  @Override
  public ServerCachePlugin getServerCachePlugin() {
    return serverCachePlugin;
  }

  @Override
  public DatabaseConfig setServerCachePlugin(ServerCachePlugin serverCachePlugin) {
    this.serverCachePlugin = serverCachePlugin;
    return this;
  }

  @Override
  public boolean isEagerFetchLobs() {
    return eagerFetchLobs;
  }

  @Override
  public DatabaseConfig setEagerFetchLobs(boolean eagerFetchLobs) {
    this.eagerFetchLobs = eagerFetchLobs;
    return this;
  }

  @Override
  public int getMaxCallStack() {
    return maxCallStack;
  }

  @Override
  public DatabaseConfig setMaxCallStack(int maxCallStack) {
    this.maxCallStack = maxCallStack;
    return this;
  }

  @Override
  public boolean isTransactionRollbackOnChecked() {
    return transactionRollbackOnChecked;
  }

  @Override
  public DatabaseConfig setTransactionRollbackOnChecked(boolean transactionRollbackOnChecked) {
    this.transactionRollbackOnChecked = transactionRollbackOnChecked;
    return this;
  }

  @Override
  public int getBackgroundExecutorSchedulePoolSize() {
    return backgroundExecutorSchedulePoolSize;
  }

  @Override
  public DatabaseConfig setBackgroundExecutorSchedulePoolSize(int backgroundExecutorSchedulePoolSize) {
    this.backgroundExecutorSchedulePoolSize = backgroundExecutorSchedulePoolSize;
    return this;
  }

  @Override
  public int getBackgroundExecutorShutdownSecs() {
    return backgroundExecutorShutdownSecs;
  }

  @Override
  public DatabaseConfig setBackgroundExecutorShutdownSecs(int backgroundExecutorShutdownSecs) {
    this.backgroundExecutorShutdownSecs = backgroundExecutorShutdownSecs;
    return this;
  }

  @Override
  public BackgroundExecutorWrapper getBackgroundExecutorWrapper() {
    return backgroundExecutorWrapper;
  }

  @Override
  public DatabaseConfig setBackgroundExecutorWrapper(BackgroundExecutorWrapper backgroundExecutorWrapper) {
    this.backgroundExecutorWrapper = backgroundExecutorWrapper;
    return this;
  }

  @Override
  public int getCacheMaxSize() {
    return cacheMaxSize;
  }

  @Override
  public DatabaseConfig setCacheMaxSize(int cacheMaxSize) {
    this.cacheMaxSize = cacheMaxSize;
    return this;
  }

  @Override
  public int getCacheMaxIdleTime() {
    return cacheMaxIdleTime;
  }

  @Override
  public DatabaseConfig setCacheMaxIdleTime(int cacheMaxIdleTime) {
    this.cacheMaxIdleTime = cacheMaxIdleTime;
    return this;
  }

  @Override
  public int getCacheMaxTimeToLive() {
    return cacheMaxTimeToLive;
  }

  @Override
  public DatabaseConfig setCacheMaxTimeToLive(int cacheMaxTimeToLive) {
    this.cacheMaxTimeToLive = cacheMaxTimeToLive;
    return this;
  }

  @Override
  public int getQueryCacheMaxSize() {
    return queryCacheMaxSize;
  }

  @Override
  public DatabaseConfig setQueryCacheMaxSize(int queryCacheMaxSize) {
    this.queryCacheMaxSize = queryCacheMaxSize;
    return this;
  }

  @Override
  public int getQueryCacheMaxIdleTime() {
    return queryCacheMaxIdleTime;
  }

  @Override
  public DatabaseConfig setQueryCacheMaxIdleTime(int queryCacheMaxIdleTime) {
    this.queryCacheMaxIdleTime = queryCacheMaxIdleTime;
    return this;
  }

  @Override
  public int getQueryCacheMaxTimeToLive() {
    return queryCacheMaxTimeToLive;
  }

  @Override
  public DatabaseConfig setQueryCacheMaxTimeToLive(int queryCacheMaxTimeToLive) {
    this.queryCacheMaxTimeToLive = queryCacheMaxTimeToLive;
    return this;
  }

  @Override
  public NamingConvention getNamingConvention() {
    return namingConvention;
  }

  @Override
  public DatabaseConfig setNamingConvention(NamingConvention namingConvention) {
    this.namingConvention = namingConvention;
    return this;
  }

  @Override
  public AggregateFormulaContext aggregateFormulaContext() {
    return aggregateFormulaContext;
  }

  @Override
  public DatabaseConfig aggregateFormulaContext(AggregateFormulaContext aggregateFormulaContext) {
    this.aggregateFormulaContext = aggregateFormulaContext;
    return this;
  }

  @Override
  public boolean isAllQuotedIdentifiers() {
    return platformConfig.isAllQuotedIdentifiers();
  }

  @Override
  public DatabaseConfig setAllQuotedIdentifiers(boolean allQuotedIdentifiers) {
    platformConfig.setAllQuotedIdentifiers(allQuotedIdentifiers);
    if (allQuotedIdentifiers) {
      adjustNamingConventionForAllQuoted();
    }
    return this;
  }

  private void adjustNamingConventionForAllQuoted() {
    if (namingConvention instanceof UnderscoreNamingConvention) {
      // we need to use matching naming convention
      this.namingConvention = new MatchingNamingConvention();
    }
  }

  @Override
  public boolean isDocStoreOnly() {
    return docStoreOnly;
  }

  @Override
  public DatabaseConfig setDocStoreOnly(boolean docStoreOnly) {
    this.docStoreOnly = docStoreOnly;
    return this;
  }

  @Override
  public DocStoreConfig getDocStoreConfig() {
    return docStoreConfig;
  }

  @Override
  public DatabaseConfig setDocStoreConfig(DocStoreConfig docStoreConfig) {
    this.docStoreConfig = docStoreConfig;
    return this;
  }

  @Override
  public DbConstraintNaming getConstraintNaming() {
    return platformConfig.getConstraintNaming();
  }

  @Override
  public DatabaseConfig setConstraintNaming(DbConstraintNaming constraintNaming) {
    platformConfig.setConstraintNaming(constraintNaming);
    return this;
  }

  @Override
  public AutoTuneConfig getAutoTuneConfig() {
    return autoTuneConfig;
  }

  @Override
  public DatabaseConfig setAutoTuneConfig(AutoTuneConfig autoTuneConfig) {
    this.autoTuneConfig = autoTuneConfig;
    return this;
  }

  @Override
  public boolean skipDataSourceCheck() {
    return skipDataSourceCheck || readOnlyDatabase;
  }

  @Override
  public DatabaseConfig setSkipDataSourceCheck(boolean skipDataSourceCheck) {
    this.skipDataSourceCheck = skipDataSourceCheck;
    return this;
  }

  @Override
  public DatabaseBuilder readOnlyDatabase(boolean readOnlyDatabase) {
    this.readOnlyDatabase = readOnlyDatabase;
    return this;
  }

  @Override
  public boolean readOnlyDatabase() {
    return readOnlyDatabase;
  }

  @Override
  public DatabaseBuilder shutdownHook(boolean shutdownHook) {
    this.shutdownHook = shutdownHook;
    return this;
  }

  @Override
  public boolean shutdownHook() {
    return shutdownHook;
  }

  @Override
  public DataSource getDataSource() {
    return dataSource;
  }

  @Override
  public DatabaseConfig setDataSource(DataSource dataSource) {
    this.dataSource = dataSource;
    return this;
  }

  @Override
  public DataSource getReadOnlyDataSource() {
    return readOnlyDataSource;
  }

  @Override
  public DatabaseConfig setReadOnlyDataSource(DataSource readOnlyDataSource) {
    this.readOnlyDataSource = readOnlyDataSource;
    return this;
  }

  @Override
  public DataSourceBuilder.Settings getDataSourceConfig() {
    return dataSourceConfig;
  }

  @Override
  public DatabaseConfig setDataSourceConfig(DataSourceBuilder dataSourceConfig) {
    this.dataSourceConfig = dataSourceConfig.settings();
    return this;
  }

  @Override
  public boolean isAutoReadOnlyDataSource() {
    return autoReadOnlyDataSource;
  }

  @Override
  public DatabaseConfig setAutoReadOnlyDataSource(boolean autoReadOnlyDataSource) {
    this.autoReadOnlyDataSource = autoReadOnlyDataSource;
    return this;
  }

  @Override
  public DataSourceBuilder.Settings getReadOnlyDataSourceConfig() {
    return readOnlyDataSourceConfig;
  }

  @Override
  public DatabaseConfig setReadOnlyDataSourceConfig(DataSourceBuilder readOnly) {
    this.readOnlyDataSourceConfig = readOnly == null ? null : readOnly.settings();
    return this;
  }

  @Override
  public String getDatabaseBooleanTrue() {
    return platformConfig.getDatabaseBooleanTrue();
  }

  @Override
  public DatabaseConfig setDatabaseBooleanTrue(String databaseTrue) {
    platformConfig.setDatabaseBooleanTrue(databaseTrue);
    return this;
  }

  @Override
  public String getDatabaseBooleanFalse() {
    return platformConfig.getDatabaseBooleanFalse();
  }

  @Override
  public DatabaseConfig setDatabaseBooleanFalse(String databaseFalse) {
    this.platformConfig.setDatabaseBooleanFalse(databaseFalse);
    return this;
  }

  @Override
  public int getDatabaseSequenceBatchSize() {
    return platformConfig.getDatabaseSequenceBatchSize();
  }

  @Override
  public DatabaseConfig setDatabaseSequenceBatch(int databaseSequenceBatchSize) {
    this.platformConfig.setDatabaseSequenceBatchSize(databaseSequenceBatchSize);
    return this;
  }

  @Override
  public String getDatabasePlatformName() {
    return databasePlatformName;
  }

  @Override
  public DatabaseConfig setDatabasePlatformName(String databasePlatformName) {
    this.databasePlatformName = databasePlatformName;
    return this;
  }

  @Override
  public DatabasePlatform getDatabasePlatform() {
    return databasePlatform;
  }

  @Override
  public DatabaseConfig setDatabasePlatform(DatabasePlatform databasePlatform) {
    this.databasePlatform = databasePlatform;
    return this;
  }

  @Override
  public IdType getIdType() {
    return platformConfig.getIdType();
  }

  @Override
  public DatabaseConfig setIdType(IdType idType) {
    this.platformConfig.setIdType(idType);
    return this;
  }

  @Override
  public EncryptKeyManager getEncryptKeyManager() {
    return encryptKeyManager;
  }

  @Override
  public DatabaseConfig setEncryptKeyManager(EncryptKeyManager encryptKeyManager) {
    this.encryptKeyManager = encryptKeyManager;
    return this;
  }

  @Override
  public EncryptDeployManager getEncryptDeployManager() {
    return encryptDeployManager;
  }

  @Override
  public DatabaseConfig setEncryptDeployManager(EncryptDeployManager encryptDeployManager) {
    this.encryptDeployManager = encryptDeployManager;
    return this;
  }

  @Override
  public Encryptor getEncryptor() {
    return encryptor;
  }

  @Override
  public DatabaseConfig setEncryptor(Encryptor encryptor) {
    this.encryptor = encryptor;
    return this;
  }

  @Override
  public boolean isDbOffline() {
    return dbOffline;
  }

  @Override
  public DatabaseConfig setDbOffline(boolean dbOffline) {
    this.dbOffline = dbOffline;
    return this;
  }

  @Override
  public DbEncrypt getDbEncrypt() {
    return dbEncrypt;
  }

  @Override
  public DatabaseConfig setDbEncrypt(DbEncrypt dbEncrypt) {
    this.dbEncrypt = dbEncrypt;
    return this;
  }

  @Override
  public PlatformConfig getPlatformConfig() {
    return platformConfig;
  }

  @Override
  public DatabaseConfig setPlatformConfig(PlatformConfig platformConfig) {
    this.platformConfig = platformConfig;
    return this;
  }

  @Override
  public DatabaseConfig setDbUuid(PlatformConfig.DbUuid dbUuid) {
    this.platformConfig.setDbUuid(dbUuid);
    return this;
  }

  @Override
  public UuidVersion getUuidVersion() {
    return uuidVersion;
  }

  @Override
  public DatabaseConfig setUuidVersion(UuidVersion uuidVersion) {
    this.uuidVersion = uuidVersion;
    return this;
  }

  @Override
  public String getUuidStateFile() {
    if (uuidStateFile == null || uuidStateFile.isEmpty()) {
      // by default, add servername...
      uuidStateFile = name + "-uuid.state";
      // and store it in the user's home directory
      String homeDir = System.getProperty("user.home");
      if (homeDir != null && homeDir.isEmpty()) {
        uuidStateFile = homeDir + "/.ebean/" + uuidStateFile;
      }
    }
    return uuidStateFile;
  }

  @Override
  public DatabaseConfig setUuidStateFile(String uuidStateFile) {
    this.uuidStateFile = uuidStateFile;
    return this;
  }

  @Override
  public String getUuidNodeId() {
    return uuidNodeId;
  }

  @Override
  public DatabaseConfig setUuidNodeId(String uuidNodeId) {
    this.uuidNodeId = uuidNodeId;
    return this;
  }

  @Override
  public boolean isLocalTimeWithNanos() {
    return localTimeWithNanos;
  }

  @Override
  public DatabaseConfig setLocalTimeWithNanos(boolean localTimeWithNanos) {
    this.localTimeWithNanos = localTimeWithNanos;
    return this;
  }

  @Override
  public boolean isDurationWithNanos() {
    return durationWithNanos;
  }

  @Override
  public DatabaseConfig setDurationWithNanos(boolean durationWithNanos) {
    this.durationWithNanos = durationWithNanos;
    return this;
  }

  @Override
  public DatabaseConfig setRunMigration(boolean runMigration) {
    this.runMigration = runMigration;
    return this;
  }

  @Override
  public boolean isRunMigration() {
    final String run = System.getProperty("ebean.migration.run");
    return (run != null) ? Boolean.parseBoolean(run) : runMigration;
  }

  @Override
  public DatabaseConfig setDdlGenerate(boolean ddlGenerate) {
    this.ddlGenerate = ddlGenerate;
    return this;
  }

  @Override
  public DatabaseConfig setDdlRun(boolean ddlRun) {
    this.ddlRun = ddlRun;
    return this;
  }

  @Override
  public DatabaseConfig setDdlExtra(boolean ddlExtra) {
    this.ddlExtra = ddlExtra;
    return this;
  }


  @Override
  public boolean isDdlCreateOnly() {
    return ddlCreateOnly;
  }

  @Override
  public DatabaseConfig setDdlCreateOnly(boolean ddlCreateOnly) {
    this.ddlCreateOnly = ddlCreateOnly;
    return this;
  }

  @Override
  public String getDdlSeedSql() {
    return ddlSeedSql;
  }

  @Override
  public DatabaseConfig setDdlSeedSql(String ddlSeedSql) {
    this.ddlSeedSql = ddlSeedSql;
    return this;
  }

  @Override
  public String getDdlInitSql() {
    return ddlInitSql;
  }

  @Override
  public DatabaseConfig setDdlInitSql(String ddlInitSql) {
    this.ddlInitSql = ddlInitSql;
    return this;
  }

  @Override
  public boolean isDdlGenerate() {
    return ddlGenerate;
  }

  @Override
  public boolean isDdlRun() {
    return ddlRun;
  }

  @Override
  public boolean isDdlExtra() {
    return ddlExtra;
  }

  @Override
  public DatabaseConfig setDdlHeader(String ddlHeader) {
    this.ddlHeader = ddlHeader;
    return this;
  }

  @Override
  public String getDdlHeader() {
    if (ddlHeader != null && !ddlHeader.isEmpty()) {
      String header = ddlHeader.replace("${version}", EbeanVersion.getVersion());
      header = header.replace("${timestamp}", ZonedDateTime.now().format(DateTimeFormatter.ISO_INSTANT));
      return header;
    }
    return ddlHeader;
  }

  @Override
  public boolean isDdlStrictMode() {
    return ddlStrictMode;
  }

  @Override
  public DatabaseConfig setDdlStrictMode(boolean ddlStrictMode) {
    this.ddlStrictMode = ddlStrictMode;
    return this;
  }

  @Override
  public String getDdlPlaceholders() {
    return ddlPlaceholders;
  }

  @Override
  public DatabaseConfig setDdlPlaceholders(String ddlPlaceholders) {
    this.ddlPlaceholders = ddlPlaceholders;
    return this;
  }

  @Override
  public Map<String, String> getDdlPlaceholderMap() {
    return ddlPlaceholderMap;
  }

  @Override
  public DatabaseConfig setDdlPlaceholderMap(Map<String, String> ddlPlaceholderMap) {
    this.ddlPlaceholderMap = ddlPlaceholderMap;
    return this;
  }

  @Override
  public boolean isDisableClasspathSearch() {
    return disableClasspathSearch;
  }

  @Override
  public DatabaseConfig setDisableClasspathSearch(boolean disableClasspathSearch) {
    this.disableClasspathSearch = disableClasspathSearch;
    return this;
  }

  @Override
  public String getJodaLocalTimeMode() {
    return jodaLocalTimeMode;
  }

  @Override
  public DatabaseConfig setJodaLocalTimeMode(String jodaLocalTimeMode) {
    this.jodaLocalTimeMode = jodaLocalTimeMode;
    return this;
  }

  @Override
  public DatabaseConfig addClass(Class<?> cls) {
    classes.add(cls);
    return this;
  }

  @Override
  public DatabaseConfig addAll(Collection<Class<?>> classList) {
    if (classList != null && !classList.isEmpty()) {
      classes.addAll(classList);
    }
    return this;
  }

  @Override
  public DatabaseConfig addPackage(String packageName) {
    packages.add(packageName);
    return this;
  }

  @Override
  public List<String> getPackages() {
    return packages;
  }

  @Override
  public DatabaseConfig setPackages(List<String> packages) {
    this.packages = packages;
    return this;
  }

  /**
   * Set the list of classes (entities, listeners, scalarTypes etc) that should
   * be used for this database.
   * <p>
   * Leaving void for spring xml wiring for now.
   */
  public void setClasses(Collection<Class<?>> classes) {
    this.classes = new HashSet<>(classes);
  }

  @Override
  public DatabaseConfig classes(Collection<Class<?>> classes) {
    this.classes = new HashSet<>(classes);
    return this;
  }

  @Override
  public Set<Class<?>> classes() {
    return classes;
  }

  /**
   * @deprecated - migrate to {@link #classes()}.
   */
  @Override
  @SuppressWarnings("removal")
  @Deprecated(forRemoval = true)
  public Set<Class<?>> getClasses() {
    return classes;
  }

  @Override
  public boolean isSkipCacheAfterWrite() {
    return skipCacheAfterWrite;
  }

  @Override
  public DatabaseConfig setSkipCacheAfterWrite(boolean skipCacheAfterWrite) {
    this.skipCacheAfterWrite = skipCacheAfterWrite;
    return this;
  }

  @Override
  public boolean isUpdateAllPropertiesInBatch() {
    return updateAllPropertiesInBatch;
  }

  @Override
  public DatabaseConfig setUpdateAllPropertiesInBatch(boolean updateAllPropertiesInBatch) {
    this.updateAllPropertiesInBatch = updateAllPropertiesInBatch;
    return this;
  }

  @Override
  public String getResourceDirectory() {
    return resourceDirectory;
  }

  @Override
  public DatabaseConfig setResourceDirectory(String resourceDirectory) {
    this.resourceDirectory = resourceDirectory;
    return this;
  }

  @Override
  public DatabaseConfig addCustomMapping(DbType type, String columnDefinition, Platform platform) {
    platformConfig.addCustomMapping(type, columnDefinition, platform);
    return this;
  }

  @Override
  public DatabaseConfig addCustomMapping(DbType type, String columnDefinition) {
    platformConfig.addCustomMapping(type, columnDefinition);
    return this;
  }

  @Override
  public DatabaseConfig add(BeanQueryAdapter beanQueryAdapter) {
    queryAdapters.add(beanQueryAdapter);
    return this;
  }

  @Override
  public List<BeanQueryAdapter> getQueryAdapters() {
    return queryAdapters;
  }

  @Override
  public DatabaseConfig setQueryAdapters(List<BeanQueryAdapter> queryAdapters) {
    this.queryAdapters = queryAdapters;
    return this;
  }

  @Override
  public List<IdGenerator> getIdGenerators() {
    return idGenerators;
  }

  @Override
  public DatabaseConfig setIdGenerators(List<IdGenerator> idGenerators) {
    this.idGenerators = idGenerators;
    return this;
  }

  @Override
  public DatabaseConfig add(IdGenerator idGenerator) {
    idGenerators.add(idGenerator);
    return this;
  }

  @Override
  public DatabaseConfig add(BeanPersistController beanPersistController) {
    persistControllers.add(beanPersistController);
    return this;
  }

  @Override
  public DatabaseConfig add(BeanPostLoad postLoad) {
    postLoaders.add(postLoad);
    return this;
  }

  @Override
  public DatabaseConfig add(BeanPostConstructListener listener) {
    postConstructListeners.add(listener);
    return this;
  }

  @Override
  public List<BeanFindController> getFindControllers() {
    return findControllers;
  }

  @Override
  public DatabaseConfig setFindControllers(List<BeanFindController> findControllers) {
    this.findControllers = findControllers;
    return this;
  }

  @Override
  public List<BeanPostLoad> getPostLoaders() {
    return postLoaders;
  }

  @Override
  public DatabaseConfig setPostLoaders(List<BeanPostLoad> postLoaders) {
    this.postLoaders = postLoaders;
    return this;
  }

  @Override
  public List<BeanPostConstructListener> getPostConstructListeners() {
    return postConstructListeners;
  }

  @Override
  public DatabaseConfig setPostConstructListeners(List<BeanPostConstructListener> listeners) {
    this.postConstructListeners = listeners;
    return this;
  }

  @Override
  public List<BeanPersistController> getPersistControllers() {
    return persistControllers;
  }

  @Override
  public DatabaseConfig setPersistControllers(List<BeanPersistController> persistControllers) {
    this.persistControllers = persistControllers;
    return this;
  }

  @Override
  public DatabaseConfig add(BeanPersistListener beanPersistListener) {
    persistListeners.add(beanPersistListener);
    return this;
  }

  @Override
  public List<BeanPersistListener> getPersistListeners() {
    return persistListeners;
  }

  @Override
  public DatabaseConfig add(BulkTableEventListener bulkTableEventListener) {
    bulkTableEventListeners.add(bulkTableEventListener);
    return this;
  }

  @Override
  public List<BulkTableEventListener> getBulkTableEventListeners() {
    return bulkTableEventListeners;
  }

  @Override
  public DatabaseConfig addServerConfigStartup(ServerConfigStartup configStartupListener) {
    configStartupListeners.add(configStartupListener);
    return this;
  }

  @Override
  public List<ServerConfigStartup> getServerConfigStartupListeners() {
    return configStartupListeners;
  }

  @Override
  public DatabaseConfig setPersistListeners(List<BeanPersistListener> persistListeners) {
    this.persistListeners = persistListeners;
    return this;
  }

  @Override
  public PersistenceContextScope getPersistenceContextScope() {
    // if somehow null return TRANSACTION scope
    return persistenceContextScope == null ? PersistenceContextScope.TRANSACTION : persistenceContextScope;
  }

  @Override
  public DatabaseConfig setPersistenceContextScope(PersistenceContextScope persistenceContextScope) {
    this.persistenceContextScope = persistenceContextScope;
    return this;
  }

  @Override
  public ClassLoadConfig getClassLoadConfig() {
    return classLoadConfig;
  }

  @Override
  public DatabaseConfig setClassLoadConfig(ClassLoadConfig classLoadConfig) {
    this.classLoadConfig = classLoadConfig;
    return this;
  }

  @Override
  public DatabaseConfig loadFromProperties() {
    this.properties = Config.asProperties();
    configureFromProperties();
    return this;
  }

  @Override
  public DatabaseConfig loadFromProperties(Properties properties) {
    // keep the properties used for configuration so that these are available for plugins
    this.properties = Config.asConfiguration().eval(properties);
    configureFromProperties();
    return this;
  }

  /**
   * Load the settings from the given properties
   */
  private void configureFromProperties() {
    List<AutoConfigure> autoConfigures = autoConfiguration();
    loadSettings(new PropertiesWrapper("ebean", name, properties, classLoadConfig));
    for (AutoConfigure autoConfigure : autoConfigures) {
      autoConfigure.postConfigure(this);
    }
  }

  /**
   * Use a 'plugin' to provide automatic configuration. Intended for automatic testing
   * configuration with Docker containers via ebean-test-config.
   */
  private List<AutoConfigure> autoConfiguration() {
    List<AutoConfigure> list = new ArrayList<>();
    for (AutoConfigure autoConfigure : ServiceLoader.load(AutoConfigure.class)) {
      autoConfigure.preConfigure(this);
      list.add(autoConfigure);
    }
    return list;
  }

  @Override
  public Properties getProperties() {
    return properties;
  }

  /**
   * loads the data source settings to preserve existing behaviour. IMHO, if someone has set the datasource config already,
   * they don't want the settings to be reloaded and reset. This allows a descending class to override this behaviour and prevent it
   * from happening.
   *
   * @param p - The defined property source passed to load settings
   */
  protected void loadDataSourceSettings(PropertiesWrapper p) {
    dataSourceConfig.loadSettings(p.properties, name);
    readOnlyDataSourceConfig.loadSettings(p.properties, name + "-ro");
  }

  /**
   * This is broken out to allow overridden behaviour.
   */
  protected void loadDocStoreSettings(PropertiesWrapper p) {
    docStoreConfig.loadSettings(p);
  }

  /**
   * This is broken out to allow overridden behaviour.
   */
  protected void loadAutoTuneSettings(PropertiesWrapper p) {
    autoTuneConfig.loadSettings(p);
  }

  /**
   * Load the configuration settings from the properties file.
   */
  protected void loadSettings(PropertiesWrapper p) {
    dbSchema = p.get("dbSchema", dbSchema);
    profilingConfig.loadSettings(p, name);
    platformConfig.loadSettings(p);
    if (platformConfig.isAllQuotedIdentifiers()) {
      adjustNamingConventionForAllQuoted();
    }
    namingConvention = createNamingConvention(p, namingConvention);
    if (namingConvention != null) {
      namingConvention.loadFromProperties(p);
    }
    if (autoTuneConfig == null) {
      autoTuneConfig = new AutoTuneConfig();
    }
    loadAutoTuneSettings(p);

    if (dataSourceConfig == null) {
      dataSourceConfig = DataSourceBuilder.create().settings();
    }
    loadDataSourceSettings(p);

    if (docStoreConfig == null) {
      docStoreConfig = new DocStoreConfig();
    }
    loadDocStoreSettings(p);

    defaultServer = p.getBoolean("defaultServer", defaultServer);
    shutdownHook = p.getBoolean("shutdownHook", shutdownHook);
    readOnlyDatabase = p.getBoolean("readOnlyDatabase", readOnlyDatabase);
    autoPersistUpdates = p.getBoolean("autoPersistUpdates", autoPersistUpdates);
    loadModuleInfo = p.getBoolean("loadModuleInfo", loadModuleInfo);
    includeLabelInSql = p.getBoolean("includeLabelInSql", includeLabelInSql);
    maxCallStack = p.getInt("maxCallStack", maxCallStack);
    dumpMetricsOnShutdown = p.getBoolean("dumpMetricsOnShutdown", dumpMetricsOnShutdown);
    dumpMetricsOptions = p.get("dumpMetricsOptions", dumpMetricsOptions);
    queryPlanTTLSeconds = p.getInt("queryPlanTTLSeconds", queryPlanTTLSeconds);
    slowQueryMillis = p.getLong("slowQueryMillis", slowQueryMillis);
    queryPlanEnable = p.getBoolean("queryPlan.enable", queryPlanEnable);
    queryPlanThresholdMicros = p.getLong("queryPlan.thresholdMicros", queryPlanThresholdMicros);
    queryPlanCapture = p.getBoolean("queryPlan.capture", queryPlanCapture);
    queryPlanCapturePeriodSecs = p.getLong("queryPlan.capturePeriodSecs", queryPlanCapturePeriodSecs);
    queryPlanCaptureMaxTimeMillis = p.getLong("queryPlan.captureMaxTimeMillis", queryPlanCaptureMaxTimeMillis);
    queryPlanCaptureMaxCount = p.getInt("queryPlan.captureMaxCount", queryPlanCaptureMaxCount);
    queryPlanExplain = p.get("queryPlan.explain", queryPlanExplain);
    docStoreOnly = p.getBoolean("docStoreOnly", docStoreOnly);
    disableL2Cache = p.getBoolean("disableL2Cache", disableL2Cache);
    localOnlyL2Cache = p.getBoolean("localOnlyL2Cache", localOnlyL2Cache);
    enabledL2Regions = p.get("enabledL2Regions", enabledL2Regions);
    notifyL2CacheInForeground = p.getBoolean("notifyL2CacheInForeground", notifyL2CacheInForeground);
    useJtaTransactionManager = p.getBoolean("useJtaTransactionManager", useJtaTransactionManager);
    useValidationNotNull = p.getBoolean("useValidationNotNull", useValidationNotNull);
    autoReadOnlyDataSource = p.getBoolean("autoReadOnlyDataSource", autoReadOnlyDataSource);
    idGeneratorAutomatic = p.getBoolean("idGeneratorAutomatic", idGeneratorAutomatic);

    backgroundExecutorSchedulePoolSize = p.getInt("backgroundExecutorSchedulePoolSize", backgroundExecutorSchedulePoolSize);
    backgroundExecutorShutdownSecs = p.getInt("backgroundExecutorShutdownSecs", backgroundExecutorShutdownSecs);
    backgroundExecutorWrapper = p.createInstance(BackgroundExecutorWrapper.class, "backgroundExecutorWrapper", backgroundExecutorWrapper);
    disableClasspathSearch = p.getBoolean("disableClasspathSearch", disableClasspathSearch);
    currentUserProvider = p.createInstance(CurrentUserProvider.class, "currentUserProvider", currentUserProvider);
    databasePlatform = p.createInstance(DatabasePlatform.class, "databasePlatform", databasePlatform);
    encryptKeyManager = p.createInstance(EncryptKeyManager.class, "encryptKeyManager", encryptKeyManager);
    encryptDeployManager = p.createInstance(EncryptDeployManager.class, "encryptDeployManager", encryptDeployManager);
    encryptor = p.createInstance(Encryptor.class, "encryptor", encryptor);
    dbEncrypt = p.createInstance(DbEncrypt.class, "dbEncrypt", dbEncrypt);
    dbOffline = p.getBoolean("dbOffline", dbOffline);
    serverCachePlugin = p.createInstance(ServerCachePlugin.class, "serverCachePlugin", serverCachePlugin);

    String packagesProp = p.get("search.packages", p.get("packages", null));
    packages = searchList(packagesProp, packages);

    skipCacheAfterWrite = p.getBoolean("skipCacheAfterWrite", skipCacheAfterWrite);
    updateAllPropertiesInBatch = p.getBoolean("updateAllPropertiesInBatch", updateAllPropertiesInBatch);

    if (p.get("batch.mode") != null || p.get("persistBatching") != null) {
      throw new IllegalArgumentException("Property 'batch.mode' or 'persistBatching' is being set but no longer used. Please change to use 'persistBatchMode'");
    }

    persistBatch = p.getEnum(PersistBatch.class, "persistBatch", persistBatch);
    persistBatchOnCascade = p.getEnum(PersistBatch.class, "persistBatchOnCascade", persistBatchOnCascade);

    int batchSize = p.getInt("batch.size", persistBatchSize);
    persistBatchSize = p.getInt("persistBatchSize", batchSize);

    persistenceContextScope = PersistenceContextScope.valueOf(p.get("persistenceContextScope", "TRANSACTION"));

    changeLogAsync = p.getBoolean("changeLogAsync", changeLogAsync);
    changeLogIncludeInserts = p.getBoolean("changeLogIncludeInserts", changeLogIncludeInserts);
    expressionEqualsWithNullAsNoop = p.getBoolean("expressionEqualsWithNullAsNoop", expressionEqualsWithNullAsNoop);
    expressionNativeIlike = p.getBoolean("expressionNativeIlike", expressionNativeIlike);

    dataTimeZone = p.get("dataTimeZone", dataTimeZone);
    asOfViewSuffix = p.get("asOfViewSuffix", asOfViewSuffix);
    asOfSysPeriod = p.get("asOfSysPeriod", asOfSysPeriod);
    historyTableSuffix = p.get("historyTableSuffix", historyTableSuffix);
    jdbcFetchSizeFindEach = p.getInt("jdbcFetchSizeFindEach", jdbcFetchSizeFindEach);
    jdbcFetchSizeFindList = p.getInt("jdbcFetchSizeFindList", jdbcFetchSizeFindList);
    databasePlatformName = p.get("databasePlatformName", databasePlatformName);
    lengthCheck = p.getEnum(LengthCheck.class, "lengthCheck", lengthCheck);

    uuidVersion = p.getEnum(UuidVersion.class, "uuidVersion", uuidVersion);
    uuidStateFile = p.get("uuidStateFile", uuidStateFile);
    uuidNodeId = p.get("uuidNodeId", uuidNodeId);

    localTimeWithNanos = p.getBoolean("localTimeWithNanos", localTimeWithNanos);
    jodaLocalTimeMode = p.get("jodaLocalTimeMode", jodaLocalTimeMode);

    defaultEnumType = p.getEnum(EnumType.class, "defaultEnumType", defaultEnumType);
    disableLazyLoading = p.getBoolean("disableLazyLoading", disableLazyLoading);
    lazyLoadBatchSize = p.getInt("lazyLoadBatchSize", lazyLoadBatchSize);
    queryBatchSize = p.getInt("queryBatchSize", queryBatchSize);

    jsonInclude = p.getEnum(JsonConfig.Include.class, "jsonInclude", jsonInclude);
    jsonDateTime = p.getEnum(JsonConfig.DateTime.class, "jsonDateTime", jsonDateTime);
    jsonDate = p.getEnum(JsonConfig.Date.class, "jsonDate", jsonDate);
    jsonMutationDetection = p.getEnum(MutationDetection.class, "jsonMutationDetection", jsonMutationDetection);

    skipDataSourceCheck = p.getBoolean("skipDataSourceCheck", skipDataSourceCheck);
    runMigration = p.getBoolean("migration.run", runMigration);
    ddlGenerate = p.getBoolean("ddl.generate", ddlGenerate);
    ddlRun = p.getBoolean("ddl.run", ddlRun);
    ddlExtra = p.getBoolean("ddl.extra", ddlExtra);
    ddlCreateOnly = p.getBoolean("ddl.createOnly", ddlCreateOnly);
    ddlInitSql = p.get("ddl.initSql", ddlInitSql);
    ddlSeedSql = p.get("ddl.seedSql", ddlSeedSql);
    ddlStrictMode = p.getBoolean("ddl.strictMode", ddlStrictMode);
    ddlPlaceholders = p.get("ddl.placeholders", ddlPlaceholders);
    ddlHeader = p.get("ddl.header", ddlHeader);

    // read tenant-configuration from config:
    // tenant.mode = NONE | DB | SCHEMA | CATALOG | PARTITION
    String mode = p.get("tenant.mode");
    if (mode != null) {
      for (TenantMode value : TenantMode.values()) {
        if (value.name().equalsIgnoreCase(mode)) {
          tenantMode = value;
          break;
        }
      }
    }

    currentTenantProvider = p.createInstance(CurrentTenantProvider.class, "tenant.currentTenantProvider", currentTenantProvider);
    tenantCatalogProvider = p.createInstance(TenantCatalogProvider.class, "tenant.catalogProvider", tenantCatalogProvider);
    tenantSchemaProvider = p.createInstance(TenantSchemaProvider.class, "tenant.schemaProvider", tenantSchemaProvider);
    tenantPartitionColumn = p.get("tenant.partitionColumn", tenantPartitionColumn);
    classes = readClasses(p);

    String mappingsProp = p.get("mappingLocations", null);
    mappingLocations = searchList(mappingsProp, mappingLocations);
  }

  private NamingConvention createNamingConvention(PropertiesWrapper properties, NamingConvention namingConvention) {
    NamingConvention nc = properties.createInstance(NamingConvention.class, "namingConvention", null);
    return (nc != null) ? nc : namingConvention;
  }

  /**
   * Build the list of classes from the comma delimited string.
   *
   * @param properties the properties
   * @return the classes
   */
  private Set<Class<?>> readClasses(PropertiesWrapper properties) {
    String classNames = properties.get("classes", null);
    if (classNames == null) {
      return classes;
    }

    Set<Class<?>> classList = new HashSet<>();
    String[] split = StringHelper.splitNames(classNames);
    for (String cn : split) {
      if (!"class".equalsIgnoreCase(cn)) {
        try {
          classList.add(Class.forName(cn));
        } catch (ClassNotFoundException e) {
          String msg = "Error registering class [" + cn + "] from [" + classNames + "]";
          throw new RuntimeException(msg, e);
        }
      }
    }
    return classList;
  }

  private List<String> searchList(String searchNames, List<String> defaultValue) {
    if (searchNames != null) {
      String[] entries = StringHelper.splitNames(searchNames);
      List<String> hitList = new ArrayList<>(entries.length);
      Collections.addAll(hitList, entries);
      return hitList;
    } else {
      return defaultValue;
    }
  }

  @Override
  public PersistBatch appliedPersistBatchOnCascade() {
    if (persistBatchOnCascade == PersistBatch.INHERIT) {
      // use the platform default (ALL except SQL Server which has NONE)
      return databasePlatform.persistBatchOnCascade();
    }
    return persistBatchOnCascade;
  }

  @Override
  public Object getObjectMapper() {
    return objectMapper;
  }

  @Override
  public DatabaseConfig setObjectMapper(Object objectMapper) {
    this.objectMapper = objectMapper;
    return this;
  }

  @Override
  public boolean isExpressionEqualsWithNullAsNoop() {
    return expressionEqualsWithNullAsNoop;
  }

  @Override
  public DatabaseConfig setExpressionEqualsWithNullAsNoop(boolean expressionEqualsWithNullAsNoop) {
    this.expressionEqualsWithNullAsNoop = expressionEqualsWithNullAsNoop;
    return this;
  }

  @Override
  public boolean isExpressionNativeIlike() {
    return expressionNativeIlike;
  }

  @Override
  public DatabaseConfig setExpressionNativeIlike(boolean expressionNativeIlike) {
    this.expressionNativeIlike = expressionNativeIlike;
    return this;
  }

  @Override
  public String getEnabledL2Regions() {
    return enabledL2Regions;
  }

  @Override
  public DatabaseConfig setEnabledL2Regions(String enabledL2Regions) {
    this.enabledL2Regions = enabledL2Regions;
    return this;
  }

  @Override
  public boolean isDisableL2Cache() {
    return disableL2Cache;
  }

  @Override
  public DatabaseConfig setDisableL2Cache(boolean disableL2Cache) {
    this.disableL2Cache = disableL2Cache;
    return this;
  }

  @Override
  public boolean isLocalOnlyL2Cache() {
    return localOnlyL2Cache;
  }

  @Override
  public DatabaseConfig setLocalOnlyL2Cache(boolean localOnlyL2Cache) {
    this.localOnlyL2Cache = localOnlyL2Cache;
    return this;
  }

  @Override
  public boolean isUseValidationNotNull() {
    return useValidationNotNull;
  }

  @Override
  public DatabaseConfig setUseValidationNotNull(boolean useValidationNotNull) {
    this.useValidationNotNull = useValidationNotNull;
    return this;
  }

  @Override
  public boolean isNotifyL2CacheInForeground() {
    return notifyL2CacheInForeground;
  }

  @Override
  public DatabaseConfig setNotifyL2CacheInForeground(boolean notifyL2CacheInForeground) {
    this.notifyL2CacheInForeground = notifyL2CacheInForeground;
    return this;
  }

  @Override
  public int getQueryPlanTTLSeconds() {
    return queryPlanTTLSeconds;
  }

  @Override
  public String getQueryPlanExplain() {
    return queryPlanExplain;
  }

  @Override
  public DatabaseBuilder queryPlanExplain(String queryPlanExplain) {
    this.queryPlanExplain = queryPlanExplain;
    return this;
  }

  @Override
  public DatabaseConfig setQueryPlanTTLSeconds(int queryPlanTTLSeconds) {
    this.queryPlanTTLSeconds = queryPlanTTLSeconds;
    return this;
  }

  @Override
  public PlatformConfig newPlatformConfig(String propertiesPath, String platformPrefix) {
    if (properties == null) {
      properties = new Properties();
    }
    PropertiesWrapper p = new PropertiesWrapper(propertiesPath, platformPrefix, properties, classLoadConfig);
    PlatformConfig config = new PlatformConfig(platformConfig);
    config.loadSettings(p);
    return config;
  }

  @Override
  public DatabaseConfig addMappingLocation(String mappingLocation) {
    if (mappingLocations == null) {
      mappingLocations = new ArrayList<>();
    }
    mappingLocations.add(mappingLocation);
    return this;
  }

  @Override
  public List<String> getMappingLocations() {
    return mappingLocations;
  }

  @Override
  public DatabaseConfig setMappingLocations(List<String> mappingLocations) {
    this.mappingLocations = mappingLocations;
    return this;
  }

  @Override
  public boolean isIdGeneratorAutomatic() {
    return idGeneratorAutomatic;
  }

  @Override
  public DatabaseConfig setIdGeneratorAutomatic(boolean idGeneratorAutomatic) {
    this.idGeneratorAutomatic = idGeneratorAutomatic;
    return this;
  }

  @Override
  public boolean isQueryPlanEnable() {
    return queryPlanEnable;
  }

  @Override
  public DatabaseConfig setQueryPlanEnable(boolean queryPlanEnable) {
    this.queryPlanEnable = queryPlanEnable;
    return this;
  }

  @Override
  public long getQueryPlanThresholdMicros() {
    return queryPlanThresholdMicros;
  }

  @Override
  public DatabaseConfig setQueryPlanThresholdMicros(long queryPlanThresholdMicros) {
    this.queryPlanThresholdMicros = queryPlanThresholdMicros;
    return this;
  }

  @Override
  public boolean isQueryPlanCapture() {
    return queryPlanCapture;
  }

  @Override
  public DatabaseConfig setQueryPlanCapture(boolean queryPlanCapture) {
    this.queryPlanCapture = queryPlanCapture;
    return this;
  }

  @Override
  public long getQueryPlanCapturePeriodSecs() {
    return queryPlanCapturePeriodSecs;
  }

  @Override
  public DatabaseConfig setQueryPlanCapturePeriodSecs(long queryPlanCapturePeriodSecs) {
    this.queryPlanCapturePeriodSecs = queryPlanCapturePeriodSecs;
    return this;
  }

  @Override
  public long getQueryPlanCaptureMaxTimeMillis() {
    return queryPlanCaptureMaxTimeMillis;
  }

  @Override
  public DatabaseConfig setQueryPlanCaptureMaxTimeMillis(long queryPlanCaptureMaxTimeMillis) {
    this.queryPlanCaptureMaxTimeMillis = queryPlanCaptureMaxTimeMillis;
    return this;
  }

  @Override
  public int getQueryPlanCaptureMaxCount() {
    return queryPlanCaptureMaxCount;
  }

  @Override
  public DatabaseConfig setQueryPlanCaptureMaxCount(int queryPlanCaptureMaxCount) {
    this.queryPlanCaptureMaxCount = queryPlanCaptureMaxCount;
    return this;
  }

  @Override
  public QueryPlanListener getQueryPlanListener() {
    return queryPlanListener;
  }

  @Override
  public DatabaseConfig setQueryPlanListener(QueryPlanListener queryPlanListener) {
    this.queryPlanListener = queryPlanListener;
    return this;
  }

  @Override
  public boolean isDumpMetricsOnShutdown() {
    return dumpMetricsOnShutdown;
  }

  @Override
  public DatabaseConfig setDumpMetricsOnShutdown(boolean dumpMetricsOnShutdown) {
    this.dumpMetricsOnShutdown = dumpMetricsOnShutdown;
    return this;
  }

  @Override
  public String getDumpMetricsOptions() {
    return dumpMetricsOptions;
  }

  @Override
  public DatabaseConfig setDumpMetricsOptions(String dumpMetricsOptions) {
    this.dumpMetricsOptions = dumpMetricsOptions;
    return this;
  }

  @Override
  public boolean isLoadModuleInfo() {
    return loadModuleInfo;
  }

  @Override
  public boolean isIncludeLabelInSql() {
    return includeLabelInSql;
  }

  /**
   * @deprecated - migrate to {@link #isLoadModuleInfo()}.
   */
  @Override
  @SuppressWarnings("removal")
  @Deprecated(forRemoval = true)
  public boolean isAutoLoadModuleInfo() {
    return loadModuleInfo;
  }

  @Override
  public DatabaseConfig setLoadModuleInfo(boolean loadModuleInfo) {
    this.loadModuleInfo = loadModuleInfo;
    return this;
  }

  @Override
  public DatabaseConfig includeLabelInSql(boolean includeLabelInSql) {
    this.includeLabelInSql = includeLabelInSql;
    return this;
  }

  @Override
  public Function<String, String> getMetricNaming() {
    return metricNaming;
  }

  @Override
  public DatabaseConfig setMetricNaming(Function<String, String> metricNaming) {
    this.metricNaming = metricNaming;
    return this;
  }

  /**
   * Returns the length check mode.
   */
  public LengthCheck getLengthCheck() {
    return lengthCheck;
  }

  @Override
  public DatabaseConfig lengthCheck(LengthCheck lengthCheck) {
    this.lengthCheck = lengthCheck;
    return this;
  }

  public enum UuidVersion {
    VERSION4,
    VERSION1,
    VERSION1RND
  }
}
